package cl.darksigns.webstat.client.connection;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;


import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;


public class HttpConnector {

	private String validProtocols[] = {
			"http", "https", "ftp", "telnet", "news", "gopher"
	};
	
	private String _defaultProt = "http";
	private int _defaultPort = 80;
	
	private String prot;
	private String host;
	private int port;
	private int defaultTimeout = 1000;
	private int timeout = 0; // default (not used)
	
	private Cookie cookies[];
	private Header responseHeaders[];
	private Header requestHeaders[];
    
	/**
	 * Server address. Default protocol is http and default port is 80
	 * 
	 * @param host
	 */
	public HttpConnector(String host) {
		this.host = host;
		this.port = _defaultPort;
		this.prot = _defaultProt;
	}
	
	/**
	 * Server address and port. Default protocol is http.
	 * @param host
	 * @param port
	 */
	public HttpConnector(String host, int port) {
		this.host = host;
		this.port = port;
		this.prot = _defaultProt;
	}

	/**
	 * 
	 * @param prot
	 * @param host
	 * @param port
	 */
	public HttpConnector(String prot, String host, int port) {
		this.host = host;
		this.port = port;
		this.prot = prot;
	}

	/**
	 * doGet to relative or full url.
	 * 
	 * @param url
	 * @param relativeURL
	 * @return
	 * @throws Exception
	 */
	public String doGet(String url, boolean relativeURL) throws Exception {
		if (relativeURL) {
			if (url.charAt(0) != '/') {
				url = prot + "://" + host + ":" + port + "/" + url;
			} else {
				url = prot + "://" + host + ":" + port + url;
			}
		}
		
		HttpClient client = new HttpClient();
		GetMethod method = new GetMethod(url);
		
		// Set cookies
		if (cookies != null) {
			HttpState state = new HttpState();
			state.addCookies(cookies);
			client.setState(state);
			client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
		}
		
		
		if (timeout == 0) {
			timeout = defaultTimeout;
		}
		client.getHttpConnectionManager().getParams().setConnectionTimeout(timeout);
		client.getHttpConnectionManager().getParams().setSoTimeout(timeout);
		
		String responseBody = null;
		try {
			client.executeMethod(method);
			// if is redirect then redirect to new location through get method
			setCookies(client.getState().getCookies());
			if (isRedirect(method)) {
				String urlLocation = getLocation(method);
				boolean isRelative = isRelative(urlLocation);
				setHeaders(method);
				method.releaseConnection();
				client.getHttpConnectionManager().closeIdleConnections(0);
				return doGet(urlLocation, isRelative);
			} else {
				responseBody = method.getResponseBodyAsString();
				method.releaseConnection();
				client.getHttpConnectionManager().closeIdleConnections(0);
				return responseBody;
			}
		} catch (HttpException e) {
			e.printStackTrace();
			throw new Exception(e.getMessage());
		} catch (IOException e) {
			e.printStackTrace();
			throw new Exception(e.getMessage());
		}
	}	
	
	/**
	 * doPost to relative or full url.
	 * 
	 * @param url
	 * @param formData
	 * @param relativeURL
	 * @return
	 * @throws Exception
	 */
	public String doPost(String url,Hashtable formData, boolean relativeURL) throws Exception {
		if (relativeURL) {
			if (url.charAt(0) != '/') {
				url = prot + "://" + host + ":" + port + "/" + url;
			} else {
				url = prot + "://" + host + ":" + port + url;
			}
		}
        
		HttpClient client = new HttpClient();
		PostMethod method = new PostMethod(url);
		
		// Set cookies
		if (cookies != null) {
			HttpState state = new HttpState();
			state.addCookies(cookies);
			client.setState(state);
			client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
		}
		// set headers
		if (requestHeaders != null) {
			for (int h = 0; h < requestHeaders.length; h++) {
				method.addRequestHeader(requestHeaders[h]);
			}
		}
		
		if (timeout == 0) {
			timeout = defaultTimeout;
		}
		client.getHttpConnectionManager().getParams().setConnectionTimeout(timeout);
		client.getHttpConnectionManager().getParams().setSoTimeout(timeout);
		client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
		
		/**
		 * Form DATA
		 */
		NameValuePair pairs[] = new NameValuePair[formData.size()];
		Enumeration keys   = formData.keys();
		Enumeration values = formData.elements();
		int p = 0;
		while (keys.hasMoreElements() && values.hasMoreElements()) {
			String key = (String)keys.nextElement();
			String value = values.nextElement().toString();
			pairs[p++] = new NameValuePair(key, value);
		}
		method.setRequestBody(pairs);
		
		String responseBody = null;
		try {
			client.executeMethod(method);
			// if is redirect then redirect to new location through get method
			setCookies(client.getState().getCookies());
			if (isRedirect(method)) {
				String urlLocation = getLocation(method);
				boolean isRelative = isRelative(urlLocation);
				setHeaders(method);
				method.releaseConnection();
				client.getHttpConnectionManager().closeIdleConnections(0);
				return doGet(urlLocation, isRelative);
			} else {
				responseBody = method.getResponseBodyAsString();
				method.releaseConnection();
				client.getHttpConnectionManager().closeIdleConnections(0);
				return responseBody;
			}
		} catch (HttpException e) {
			e.printStackTrace();
			throw new Exception(e.getMessage());
		} catch (IOException e) {
			e.printStackTrace();
			throw new Exception(e.getMessage());
		}
	}

	/**
	 * 
	 * @return
	 */
	public Cookie[] getCookies() {
		return this.cookies;
	}

	/**
	 * 
	 * @param header
	 */
	public void addRequestHeader(Header header) {
		int len = requestHeaders != null ? requestHeaders.length : 0;
		
		if (len == 0) {
			requestHeaders = new Header[1];
			requestHeaders[0] = header;
		} else {
			Header tmp[] = new Header[len];
			for (int r = 0; r < len; r++) {
				tmp[r] = requestHeaders[r];
			}
			requestHeaders = new Header[len+1];
			for (int r = 0; r < len; r++) {
				requestHeaders[r] = tmp[r];
			}
			requestHeaders[len] = header;
		}
	}

	/**
	 * 
	 * @return
	 */
	public Header[] getRequestHeaders() {
		return this.requestHeaders;
	}
	
	/**
	 * 
	 * @return
	 */
	public Header[] getResponseHeaders() {
		return this.responseHeaders;
	}

	/**
	 * 
	 *
	 */
	public void removeAllCookies() {
		this.cookies = null;
	}

	/**
	 * 
	 *
	 */
	public void removeAllHeaders() {
		this.requestHeaders = null;
		this.responseHeaders = null;
	}

	/**
	 * 
	 * @param host
	 * @param port
	 */
	public void setProxySettings(String host, int port) {
		throw new IllegalArgumentException("Not implemented yet.");
	}
	
	public void setProxySettings(String host, int port, String user, String passwd) {
		throw new IllegalArgumentException("Not implemented yet.");
	}

	/**
	 * 
	 * @param timeout
	 */
	public void setConnectionTimeout(int timeout) {
		this.timeout = timeout;
	}
	
	/**
	 * PRIVATE AND UTILS METHODS
	 */
	
	private boolean isRedirect(HttpMethod method) {
		if (method.getStatusCode() == 302 || method.getStatusCode() == 303) {
			return true;
		}
		return false;
	}
	
	private boolean isRelative(String url) {
		for (int v = 0; v < validProtocols.length; v++) {
			if (url.toLowerCase().startsWith(validProtocols[v] + "://")) {
				return false;
			}
		}
		return true;
	}
	
	private String getLocation(HttpMethod method) 
	{
		Header headers[] = method.getResponseHeaders();
		for (int h = 0; h < headers.length; h++) {
			if (headers[h].getName().equals("Location")) {
				return headers[h].getValue();
			}
		}
		
		return null;
	}
	
	private void setCookies(Cookie cookies[]) {
		this.cookies = cookies;
	}
	
	private void setHeaders(HttpMethod method) {
		responseHeaders = method.getResponseHeaders();
		requestHeaders  = method.getRequestHeaders();
	}

	
	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
	}
}
